package cn.iocoder.yudao.module.wallet.utils;

import cn.hutool.core.util.RandomUtil;
import jakarta.annotation.Nonnull;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.crypto.MnemonicException;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.web3j.crypto.Bip39Wallet;
import org.web3j.crypto.Bip44WalletUtils;
import org.web3j.crypto.CipherException;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;

/**
 * @author liutao
 * @version 1.0
 * @description: TODO
 * @date 2024/10/30 19:44
 */
public final class Bip44Util {

    /**
     * generateMnemonic 12 Words
     *
     * @param
     * @return
     * @throws Exception
     */
    public static List<String> generateMnemonicWords() throws MnemonicException.MnemonicLengthException {
        SecureRandom secureRandom = RandomUtil.getSecureRandom();
        byte[] entropy = new byte[DeterministicSeed.DEFAULT_SEED_ENTROPY_BITS / 8];
        secureRandom.nextBytes(entropy);
        //生成12位助记词
        return MnemonicCode.INSTANCE.toMnemonic(entropy);
    }

    public static String convertMnemonicList(List<String> mnemonics) {
        StringBuilder sb = new StringBuilder();
        int size =  mnemonics.size();

        for (int i = 0; i < size; i++) {
            sb.append(mnemonics.get(i));
            if (i != size - 1) {
                sb.append(" ");
            }
        }

        return sb.toString();
    }

    /**
     * @param words
     * @param path  m / purpose' / coin_type' / account' / change / address_index
     * @return
     */
    public static BigInteger getPathPrivateKey(List<String> words, String path) {
        return getDeterministicKey(words, null, path).getPrivKey();
    }

    /**
     * @param words
     * @param seed
     * @param path  m / purpose' / coin_type' / account' / change / address_index
     * @return
     */
    public static BigInteger getPathPrivateKey(List<String> words, byte[] seed, String path) {
        return getDeterministicKey(words, seed, path).getPrivKey();
    }

    /**
     * @param words
     * @param path  m / purpose' / coin_type' / account' / change / address_index
     * @return
     */
    public static byte[] getPathPrivateKeyBytes(List<String> words, String path) {
        return getDeterministicKey(words, null, path).getPrivKeyBytes();
    }

    /**
     * @param words
     * @param seed
     * @param path  m / purpose' / coin_type' / account' / change / address_index
     * @return
     */
    public static byte[] getPathPrivateKeyBytes(List<String> words, byte[] seed, String path) {
        return getDeterministicKey(words, seed, path).getPrivKeyBytes();
    }

    /**
     * @param words
     * @param coinType see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
     * @return
     */
    public static BigInteger getDefaultPathPrivateKey(List<String> words, int coinType) {
        String path = "m/44'/" +
                coinType +
                "'/0'/0/0";
        return getDeterministicKey(words, null, path).getPrivKey();
    }

    /**
     * @param words
     * @param seed
     * @param coinType see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
     * @return
     */
    public static BigInteger getDefaultPathPrivateKey(List<String> words, byte[] seed, int coinType) {
        String path = "m/44'/" +
                coinType +
                "'/0'/0/0";
        return getDeterministicKey(words, seed, path).getPrivKey();
    }

    /**
     * @param words
     * @param coinType see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
     * @return
     */
    public static byte[] getDefaultPathPrivateKeyBytes(List<String> words, int coinType) {
        String path = "m/44'/" +
                coinType +
                "'/0'/0/0";
        return getDeterministicKey(words, null, path).getPrivKeyBytes();
    }

    /**
     * @param words
     * @param seed
     * @param coinType see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
     * @return
     */
    public static byte[] getDefaultPathPrivateKeyBytes(List<String> words, byte[] seed, int coinType) {
        String path = "m/44'/" +
                coinType +
                "'/0'/0/0";
        return getDeterministicKey(words, seed, path).getPrivKeyBytes();
    }

    private static DeterministicKey getDeterministicKey(List<String> words, byte[] seed, String path) {
        DeterministicSeed deterministicSeed = new DeterministicSeed(words, seed, "", 0);
        DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();
        return deterministicKeyChain.getKeyByPath(parsePath(path), true);
    }

    private static List<ChildNumber> parsePath(@Nonnull String path) {
        String[] parsedNodes = path.replace("m", "").split("/");
        List<ChildNumber> nodes = new ArrayList<>();

        for (String n : parsedNodes) {
            n = n.replaceAll(" ", "");
            if (n.isEmpty()) {
                continue;
            }
            boolean isHard = n.endsWith("'");
            if (isHard) n = n.substring(0, n.length() - 1);
            int nodeNumber = Integer.parseInt(n);
            nodes.add(new ChildNumber(nodeNumber, isHard));
        }
        System.out.println(nodes);

        return nodes;
    }
    private static String generateNewWalletName() {
        char letter1 = (char) (int) (Math.random() * 26 + 97);
        char letter2 = (char) (int) (Math.random() * 26 + 97);

        String walletName = letter1 + String.valueOf(letter2) + "-新钱包";
        return walletName;
    }

    public static void main(String[] args) throws MnemonicException.MnemonicLengthException, CipherException, IOException {
//        Bip39Wallet bip39Wallet = Bip44WalletUtils.generateBip44Wallet("123456", new File("/Volumes/extreme/999/blockchain"));
//        System.out.println(bip39Wallet.getMnemonic());
        List<String> mm = generateMnemonicWords();
        System.out.println(convertMnemonicList(mm));

        System.out.println(getDefaultPathPrivateKey(mm, 60));
        System.out.println(generateNewWalletName());
//        System.out.println(getDefaultPathPrivateKey(mm, 0x80000000));
    }
}
